﻿/*
   Open Intel 
   Copyright © 2011 – ISC. All Rights Reserved.
*/

using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Threading;
using OI.DataModel;

namespace OI.Web.DataServices
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class CatalogService
    {
        const int Retries = 3;


        [OperationContract]
        public string GetJurisdictions()
        {
            Func<string> act = () =>
            {
                string json;
                using (var dc = new OIDataContext(Global.OIConnectionString))
                {
                    var row = (from r in dc.OI_Jurisdictions
                               select new OIJurisdiction
                               {
                                   ID = r.ID,
                                   JurisdictionID = r.JurisdictionID,
                                   Customer = r.Customer,
                                   TitleBlock = r.TitleBlock,
                                   Country = r.Country,
                                   State = r.State,
                                   StateCode = r.StateCode,
                                   City = r.City,
                                   MashupMapID = r.MashupMapID,
                                   CustomerImageURI = r.CustomerImageURI
                               }).ToArray();

                    json = MessageSerializer.Serialize(row);
                }
                return json;
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string GetJurisdiction(string jurisdictionID)
        {
            Func<string> act = () =>
            {
                string json;
                using (var dc = new OIDataContext(Global.OIConnectionString))
                {
                    var row = (from r in dc.OI_Jurisdictions
                               where r.JurisdictionID == jurisdictionID
                               select new OIJurisdiction 
                               { 
                                    ID = r.ID,
                                    JurisdictionID = r.JurisdictionID,
                                    Customer = r.Customer,
                                    TitleBlock = r.TitleBlock,
                                    Country = r.Country,
                                    State = r.State,
                                    StateCode = r.StateCode,
                                    City = r.City,
                                    MashupMapID = r.MashupMapID,
                                    CustomerImageURI = r.CustomerImageURI
                               }).ToArray();

                    json = MessageSerializer.Serialize(row);
                }
                return json;
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string RemoveJurisdiction(int id, string connectionString)
        {
            Func<string> act = () =>
            {
                using (var dc = new OIDataContext(connectionString))
                {
                    var row = (from r in dc.OI_Jurisdictions
                               where r.ID == id
                               select r).FirstOrDefault();
                    if (row != null)
                    {
                        dc.OI_Jurisdictions.DeleteOnSubmit(row);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string SetJurisdiction(string serialJuris, string connectionString)
        {
            Func<string> act = () =>
            {
                var juris = MessageSerializer.Deserialize<OIJurisdiction>(serialJuris);
                if (juris == null)
                {
                    return "could not deserialize OIJurisdiction";
                }

                using (var dc = new OIDataContext(connectionString))
                {
                    // see if jurisdiction already exists since we are updating
                    if (juris.ID > 0)
                    {
                        var row = (from r in dc.OI_Jurisdictions
                                   where r.ID == juris.ID
                                   select r).FirstOrDefault();
                        if (row == null)
                        {
                            return "failed to find the record you are attempting to update";
                        }

                        row.JurisdictionID = juris.JurisdictionID;
                        row.City = juris.City;
                        row.Country = juris.Country;
                        row.Customer = juris.Customer;
                        row.CustomerImageURI = juris.CustomerImageURI;
                        row.MashupMapID = juris.MashupMapID;
                        row.State = juris.State;
                        row.StateCode = juris.StateCode;
                        row.TitleBlock = juris.TitleBlock;

                        dc.SubmitChanges();
                    }
                    else
                    {
                        var newItem = new OI_Jurisdiction
                        {
                            JurisdictionID = juris.JurisdictionID,
                            City = juris.City,
                            Country = juris.Country,
                            Customer = juris.Customer,
                            CustomerImageURI = juris.CustomerImageURI,
                            MashupMapID = juris.MashupMapID,
                            State = juris.State,
                            StateCode = juris.StateCode,
                            TitleBlock = juris.TitleBlock
                        };

                        dc.OI_Jurisdictions.InsertOnSubmit(newItem);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string GetCategories(int jurisdictionFK)
        {
            Func<string> act = () =>
            {
                string json;
                using (var dc = new OIDataContext(Global.OIConnectionString))
                {
                    var rows = (from r in dc.OI_Categories
                                where r.JurisdictionID == jurisdictionFK
                                select new OICategory 
                                { 
                                    ID = r.ID, 
                                    Name = r.Name, 
                                    JurisdictionFK = r.JurisdictionID
                                }).OrderBy(x => x.Name).ToArray();

                    json = MessageSerializer.Serialize(rows);
                }
                return json;
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string GetSources(int jurisdictionFK)
        {
            Func<string> act = () =>
            {
                string json;
                using (var dc = new OIDataContext(Global.OIConnectionString))
                {
                    var rows = (from r in dc.OI_Sources
                                where r.JurisdictionID == jurisdictionFK
                                select new OISource 
                                { 
                                    ID = r.ID, 
                                    Name = r.Name, 
                                    Filter = r.Filter,
                                    JurisdictionFK = r.JurisdictionID
                                }).OrderBy(x => x.Name).ToArray();

                    json = MessageSerializer.Serialize(rows);
                }
                return json;
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string GetDatasets(int jurisdictionFK, bool showUnpublished)
        {
            Func<string> act = () =>
            {
                string json;
                var noDate = new DateTime(1900, 1, 1);
                using (var dc = new OIDataContext(Global.OIConnectionString))
                {
                    var rows = (from r in dc.OI_Datasets
                                from c in dc.OI_Categories
                                from s in dc.OI_Sources
                                let datePub = r.PublishedDate.HasValue ? r.PublishedDate.Value : noDate
                                where c.ID == r.FK_CategoryID && 
                                s.ID == r.FK_SourceID && 
                                ((r.IsPublished.HasValue ? r.IsPublished.Value : true) || showUnpublished) &&
                                r.JurisdictionID == jurisdictionFK
                                select new OIDataset
                                {
                                    // ids
                                    ID = r.ID,
                                    JurisdictionFK = r.JurisdictionID,
                                    CategoryID = r.FK_CategoryID,
                                    SourceID = r.FK_SourceID,

                                    // attributes
                                    Name = r.Name,
                                    CategoryName = c.Name,
                                    SourceName = s.Name,
                                    SourceFilter = s.Filter,
                                    PublishedDate = datePub,
                                    Description = r.Description,
                                    Tags = r.Tags,
                                    UpdateFrequency = r.UpdateFrequency,
                                    TableName = r.TableName,
                                    LayerID = r.LayerID,
                                    ContactEmail = r.ContactEmail,
                                    IsPublished = r.IsPublished.HasValue ? r.IsPublished.Value : false,
                                    IsSpatial = r.IsSpatial.HasValue ? r.IsSpatial.Value : false
                                })
                                .OrderBy(x => x.Name).ToArray();
                    json = MessageSerializer.Serialize(rows);
                }
                return json;
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string RemoveDataset(int id, string connectionString)
        {
            Func<string> act = () =>
            {
                using (var dc = new OIDataContext(connectionString))
                {
                    var row = (from r in dc.OI_Datasets
                               where r.ID == id
                               select r).FirstOrDefault();
                    if (row != null)
                    {
                        dc.OI_Datasets.DeleteOnSubmit(row);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string SetDataset(string serialDataset, string connectionString)
        {
            Func<string> act = () =>
            {
                var ds = MessageSerializer.Deserialize<OIDataset>(serialDataset);
                if (ds == null)
                {
                    return "could not deserialize OIDataset";
                }

                using (var dc = new OIDataContext(connectionString))
                {
                    // see if dataset already exists since we are updating
                    if (ds.ID > 0)
                    {
                        var row = (from r in dc.OI_Datasets
                                   where r.ID == ds.ID
                                   select r).FirstOrDefault();
                        if (row == null)
                        {
                            return "failed to find the record you are attempting to update";
                        }
                        row.JurisdictionID = ds.JurisdictionFK;
                        row.ContactEmail = ds.ContactEmail;
                        row.Description = ds.Description;
                        row.FK_CategoryID = ds.CategoryID;
                        row.FK_SourceID = ds.SourceID;
                        row.IsPublished = ds.IsPublished;
                        row.IsSpatial = ds.IsSpatial;
                        row.LayerID = ds.LayerID;
                        row.Name = ds.Name;
                        row.PublishedDate = ds.PublishedDate;
                        row.TableName = ds.TableName;
                        row.Tags = ds.Tags;
                        row.UpdateFrequency = ds.UpdateFrequency;

                        dc.SubmitChanges();
                    }
                    // else we are inserting
                    else
                    {
                        var newItem = new OI_Dataset
                        {
                            JurisdictionID = ds.JurisdictionFK,
                            ContactEmail = ds.ContactEmail,
                            Description = ds.Description,
                            FK_CategoryID = ds.CategoryID,
                            FK_SourceID = ds.SourceID,
                            IsPublished = ds.IsPublished,
                            IsSpatial = ds.IsSpatial,
                            LayerID = ds.LayerID,
                            Name = ds.Name,
                            PublishedDate = ds.PublishedDate,
                            TableName = ds.TableName,
                            Tags = ds.Tags,
                            UpdateFrequency = ds.UpdateFrequency
                        };

                        dc.OI_Datasets.InsertOnSubmit(newItem);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string GetApps(int jurisdictionFK)
        {
            Func<string> act = () =>
            {
                string json;
                using (var dc = new OIDataContext(Global.OIConnectionString))
                {
                    var rows = (from r in dc.OI_Apps
                                where r.JurisdictionID == jurisdictionFK
                                select new OIApp 
                                { 
                                    ID = r.ID,
                                    JurisdictionFK = r.JurisdictionID,
                                    ShareID = r.ShareID, 
                                    Title = r.Title, 
                                    Description = r.Description, 
                                    ThumbnailURI = r.ThumbnailURI,
                                    UriOverride = r.UriOverride
                                }).OrderBy(x => x.Title).ToArray();

                    json = MessageSerializer.Serialize(rows);
                }
                return json;
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string RemoveApp(int id, string connectionString)
        {
            Func<string> act = () =>
            {
                using (var dc = new OIDataContext(connectionString))
                {
                    var row = (from r in dc.OI_Apps
                               where r.ID == id
                               select r).FirstOrDefault();
                    if (row != null)
                    {
                        dc.OI_Apps.DeleteOnSubmit(row);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string SetApp(string serialApp, string connectionString)
        {
            Func<string> act = () =>
            {
                var app = MessageSerializer.Deserialize<OIApp>(serialApp);
                if (app == null)
                {
                    return "could not deserialize OIApp";
                }

                using (var dc = new OIDataContext(connectionString))
                {
                    // see if app already exists since we are updating
                    if (app.ID > 0)
                    {
                        var row = (from r in dc.OI_Apps
                                   where r.ID == app.ID
                                   select r).FirstOrDefault();
                        if (row == null)
                        {
                            return "failed to find the record you are attempting to update";
                        }
                        row.JurisdictionID = app.JurisdictionFK;
                        row.Description = app.Description;
                        row.ShareID = app.ShareID;
                        row.ThumbnailURI = app.ThumbnailURI;
                        row.UriOverride = app.UriOverride;
                        row.Title = app.Title;

                        dc.SubmitChanges();
                    }
                    else
                    {
                        var newItem = new OI_App
                        {
                            JurisdictionID = app.JurisdictionFK,
                            Description = app.Description,
                            ShareID = app.ShareID,
                            ThumbnailURI = app.ThumbnailURI,
                            UriOverride = app.UriOverride,
                            Title = app.Title
                        };

                        dc.OI_Apps.InsertOnSubmit(newItem);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string RemoveCategory(int id, string connectionString)
        {
            Func<string> act = () =>
            {
                using (var dc = new OIDataContext(connectionString))
                {
                    var row = (from r in dc.OI_Categories
                               where r.ID == id
                               select r).FirstOrDefault();
                    if (row != null)
                    {
                        dc.OI_Categories.DeleteOnSubmit(row);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string AddCategory(int jurisdictionFK, string name, string connectionString)
        {
            Func<string> act = () =>
            {
                using (var dc = new OIDataContext(connectionString))
                {
                    var newItem = new OI_Category
                    {
                        JurisdictionID = jurisdictionFK,
                        Name = name
                    };

                    dc.OI_Categories.InsertOnSubmit(newItem);
                    dc.SubmitChanges();
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string RemoveSource(int id, string connectionString)
        {
            Func<string> act = () =>
            {
                using (var dc = new OIDataContext(connectionString))
                {
                    var row = (from r in dc.OI_Sources
                               where r.ID == id
                               select r).FirstOrDefault();
                    if (row != null)
                    {
                        dc.OI_Sources.DeleteOnSubmit(row);
                        dc.SubmitChanges();
                    }
                }
                return "";
            };
            return TryWithRetry(act);
        }


        [OperationContract]
        public string AddSource(int jurisdictionFK, string name, string filter, string connectionString)
        {
            Func<string> act = () =>
            {
                using (var dc = new OIDataContext(connectionString))
                {
                    var newItem = new OI_Source
                    {
                        JurisdictionID = jurisdictionFK,
                        Name = name,
                        Filter = filter
                    };

                    dc.OI_Sources.InsertOnSubmit(newItem);
                    dc.SubmitChanges();
                }
                return "";
            };
            return TryWithRetry(act);
        }


        // tries an action several times before failing
        // necessary for back-ends such as SQL Azure
        private static string TryWithRetry(Func<string> act)
        {
            int rc = 0;
            string lastErr = null;

            while (rc < Retries)
            {
                try
                {
                    return act();
                }
                catch (Exception ex)
                {
                    rc++;
                    lastErr = "CatalogService Failure: " + ex.Message + " -> connection:" + Global.OIConnectionString;
                    Thread.Sleep(1000);
                }
            }
            return lastErr;
        }
    }
}
